package net

import (
	"net"
	"time"
	"strings"
	"net/url"
	"net/http"
	"io/ioutil"
	"crypto/tls"

	"abm-cache-center/conf"
	"abm-cache-center/net/netutil/breaker"
	"abm-cache-center/net/blade/logger"
)

const (
	_family = "http_client"
)

type Client struct {
	conf	  *conf.HttpClient
	client    *http.Client
	dialer    *net.Dialer
	transport http.RoundTripper
	breaker   *breaker.Group
}

func NewClient(c *conf.HttpClient) *Client {
	client := new(Client)
	client.conf = c
	client.dialer = &net.Dialer{
		Timeout:   time.Duration(c.DialTimeout),
		KeepAlive: time.Duration(c.KeepAlive),
	}
	client.transport = &http.Transport{
		DialContext:     client.dialer.DialContext,
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client.client = &http.Client{
		Transport: client.transport,
	}

	client.breaker = breaker.NewGroup(_family, c.Breaker)
	if c.Timeout > 0 {
		c.Timeout = 0
	}

	return client
}

func (c *Client) Do(method string, requestUrl string, params string, header map[string]string) (string, error) {
	var (
		req *http.Request
		res *http.Response
		body []byte
		code string
		err  error
	)

	// breaker
	brk := c.breaker.Get(requestUrl)
	if err = brk.Allow(); err != nil {
		code = "breaker"
		return code, err
	}

	defer c.onBreaker(brk, &err)
	if "GET" == method || method == "" {
		req, err = http.NewRequest("GET", requestUrl, nil)
	} else {
		req, err = http.NewRequest("POST", requestUrl, strings.NewReader(params))
	}

	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if header != nil {
		req.Header.Set("Host", header["host"])
	}

	res, err = c.client.Do(req)
	if err != nil {
		return "", err
	}

	defer res.Body.Close()
	body, err = ioutil.ReadAll(res.Body)
	if err != nil {
		return "", err
	}

	return string(body), nil
}

// parse url and set it's ip
func (c *Client) ParseUrl(requestUrl string, ip string) (result map[string]string) {
	var (
		path string
	)
	urlData, err := url.Parse(requestUrl)
	if err != nil {
		logger.Error("parse url(%v)", err)
		return
	}

	if urlData.Path != "" {
		path = urlData.Path
	}
	if urlData.RawQuery != "" {
		path = path + "?" + urlData.RawQuery
	}

	if result == nil {
		result = make(map[string]string, 2)
		result["url"] = urlData.Scheme + "://" + ip + path
		result["header"] = "Host:" + urlData.Host
	}

	return result
}

func (c *Client) onBreaker(breaker breaker.Breaker, err *error) {
	if err != nil && *err != nil {
		breaker.MarkFailed()
	} else {
		breaker.MarkSuccess()
	}
}